// Copyright (C) 1953-2024 NUDT
// Verilog module name - multicast_packet_lookup
// Version: V4.3.0.20240125
// Created:
//         by - fenglin
////////////////////////////////////////////////////////////////////////////
// Description:
//     multicast_lookup   
///////////////////////////////////////////////////////////////////////////

module multicast_lookup(                        
    i_clk                                   ,
    i_rst_n                                 ,

    i_fifo_empty_fifo2mlu                   ,
    o_fifo_rd_mlu2fifo                      ,
    iv_md_fifo2mlu                          ,

    o_ipram_rd_mlu2ram                     ,
    ov_ipram_raddr_mlu2ram                  ,
    iv_ipram_rdata_ram2mlu                  ,

    iv_droute_portbm_cpe2tlp                ,
    iv_sroute_portbm_cpe2tlp                ,
    i_unknown_multicast_forwardmode_cpe2tlp ,

    ov_md_mpl2tlp                           ,
    o_md_wr_mpl2tlp                         
);

// I/O
input   wire                i_clk                                   ;
input   wire                i_rst_n                                 ;

input   wire                i_fifo_empty_fifo2mlu                   ;
output  reg                 o_fifo_rd_mlu2fifo                      ;
input   wire    [299:0]     iv_md_fifo2mlu                          ;

output  reg                 o_ipram_rd_mlu2ram                      ;
output  reg     [2:0]       ov_ipram_raddr_mlu2ram                  ;
input   wire    [64:0]      iv_ipram_rdata_ram2mlu                  ;

input   wire    [31:0]      iv_droute_portbm_cpe2tlp                ;
input   wire    [31:0]      iv_sroute_portbm_cpe2tlp                ;
input   wire                i_unknown_multicast_forwardmode_cpe2tlp ;

output  reg     [299:0]     ov_md_mpl2tlp                           ;
output  reg                 o_md_wr_mpl2tlp                         ;

//***************************************************
//               multicast lookup table
//***************************************************
localparam  IDLE_S                                  =   4'd0,
            ALL_NOTROUTE_NET_S                      =   4'd1,
            HCP_AND_OTHER_NOT_ROUTEEXPECT_SELF_S    =   4'd2,
            ALL_ROUTES_S                            =   4'd3,
            HCP_PORT_S                              =   4'd4,
            OTHER_KIND_HCP_PORT_S                   =   4'd5,
            WAIT_FIRST_S                            =   4'd6,
            WAIT_SECOND_S                           =   4'd7,
            LOOKUP_MLUIP_TABLE_S                    =   4'd8,
            LOOKUP_TABLE_SUCCESS_S                  =   4'd9,
            LOOKUP_TABLE_FAIL_S                     =   4'd10,
            NOT_MULTI_IP_S                          =   4'd11;


reg  [3:0] mlu_state;
wire [5:0] inport = iv_md_fifo2mlu[154:149];

always @(posedge i_clk or negedge i_rst_n) begin
    if (~i_rst_n) begin
        mlu_state <= IDLE_S;
        o_fifo_rd_mlu2fifo      <=  1'b0;
        o_ipram_rd_mlu2ram      <=  1'b0;
        ov_ipram_raddr_mlu2ram  <=  3'b0;
        ov_md_mpl2tlp           <=  300'b0;
        o_md_wr_mpl2tlp         <=  1'b0;
    end else begin
        case (mlu_state)
            IDLE_S: begin
                o_fifo_rd_mlu2fifo      <=  1'b0;
                o_ipram_rd_mlu2ram      <=  1'b0;
                ov_ipram_raddr_mlu2ram  <=  3'b0;
                ov_md_mpl2tlp           <=  300'b0;
                o_md_wr_mpl2tlp         <=  1'b0;
                if (!i_fifo_empty_fifo2mlu) begin
                    if (iv_md_fifo2mlu[298:295] == 4'b1110) begin
                        if (iv_md_fifo2mlu[266:264] == 3'b000) begin
                            o_ipram_rd_mlu2ram  <= 1'b1;
                            mlu_state           <= WAIT_FIRST_S;
                        end else if (iv_md_fifo2mlu[266:264] == 3'b001) begin
                            if ((iv_md_fifo2mlu[154:149] == 6'd4 //6'd32 
                            || iv_sroute_portbm_cpe2tlp[inport] == 1'b1)) begin
                                o_fifo_rd_mlu2fifo  <=  1'b1;
                                mlu_state           <=  ALL_NOTROUTE_NET_S;
                            end else begin
                                o_fifo_rd_mlu2fifo  <=  1'b1;
                                mlu_state           <=  HCP_AND_OTHER_NOT_ROUTEEXPECT_SELF_S;
                            end  
                        end else if (iv_md_fifo2mlu[266:264] == 3'b010 || iv_md_fifo2mlu[266:264] == 3'b011) begin
                            if (iv_md_fifo2mlu[154:149] == 6'd4 //6'd32
                            ) begin
                                o_fifo_rd_mlu2fifo  <=  1'b1;
                                mlu_state           <=  ALL_ROUTES_S;
                            end else begin
                                o_fifo_rd_mlu2fifo  <=  1'b1;
                                mlu_state           <=  HCP_PORT_S;
                            end
                        end else begin
                            o_fifo_rd_mlu2fifo  <=  1'b1;
                            mlu_state           <=  OTHER_KIND_HCP_PORT_S;
                        end
                    end else begin
                        o_fifo_rd_mlu2fifo <= 1'b1;
                        mlu_state <= NOT_MULTI_IP_S;
                    end
                end else begin
                    mlu_state <= IDLE_S;
                end
            end 
            ALL_NOTROUTE_NET_S: begin
                o_fifo_rd_mlu2fifo  <= 1'b0;
                o_md_wr_mpl2tlp     <= 1'b1;
                ov_md_mpl2tlp       <= {iv_md_fifo2mlu[299:199],1'b0,~(32'h1 << inport),iv_md_fifo2mlu[165:0]};
                mlu_state           <= IDLE_S;
            end
            HCP_AND_OTHER_NOT_ROUTEEXPECT_SELF_S: begin
                o_fifo_rd_mlu2fifo  <= 1'b0;
                o_md_wr_mpl2tlp     <= 1'b1;
                ov_md_mpl2tlp       <= {iv_md_fifo2mlu[299:199],1'b1, ~(32'h1 << inport), iv_md_fifo2mlu[165:0]};
                mlu_state           <= IDLE_S;
            end
            ALL_ROUTES_S: begin
                o_fifo_rd_mlu2fifo  <= 1'b0;
                o_md_wr_mpl2tlp     <= 1'b1;
                ov_md_mpl2tlp       <= {iv_md_fifo2mlu[299:199],1'b0,(iv_droute_portbm_cpe2tlp | iv_sroute_portbm_cpe2tlp),iv_md_fifo2mlu[165:0]};
                mlu_state           <= IDLE_S;
            end
            HCP_PORT_S: begin
                o_fifo_rd_mlu2fifo  <= 1'b0;
                o_md_wr_mpl2tlp     <= 1'b1;
                ov_md_mpl2tlp       <= {iv_md_fifo2mlu[299:199],1'b1,32'b0,iv_md_fifo2mlu[165:0]};
                mlu_state           <= IDLE_S;
            end
            OTHER_KIND_HCP_PORT_S: begin
                o_fifo_rd_mlu2fifo  <= 1'b0;
                o_md_wr_mpl2tlp     <= 1'b1;
                ov_md_mpl2tlp       <= {iv_md_fifo2mlu[299:199],1'b1,32'b0,iv_md_fifo2mlu[165:0]};
                mlu_state           <= IDLE_S;
            end
            WAIT_FIRST_S: begin
                ov_ipram_raddr_mlu2ram  <=  ov_ipram_raddr_mlu2ram + 1'b1;
                mlu_state               <=  WAIT_SECOND_S;
            end
            WAIT_SECOND_S: begin
                ov_ipram_raddr_mlu2ram  <=  ov_ipram_raddr_mlu2ram + 1'b1;
                mlu_state               <=  LOOKUP_MLUIP_TABLE_S;
            end
            LOOKUP_MLUIP_TABLE_S: begin
                    if (iv_ipram_rdata_ram2mlu[64] == 1'b1) begin
                        if (iv_ipram_rdata_ram2mlu[63-:32] == iv_md_fifo2mlu[298:267]) begin
                            ov_ipram_raddr_mlu2ram[2:0] <=  3'd0;
                            o_ipram_rd_mlu2ram          <=  1'b0;
                            o_fifo_rd_mlu2fifo          <=  1'b1;
                            ov_md_mpl2tlp[198:166]      <=  {1'b0,(~(32'h1 << inport) & iv_ipram_rdata_ram2mlu[31:0])}; //20240224 // {1'b0,iv_ipram_rdata_ram2mlu[31:0]};    //20240202
                            mlu_state                   <=  LOOKUP_TABLE_SUCCESS_S;
                        end else begin
                            if (ov_ipram_raddr_mlu2ram[2:0] != 3'd9) begin
                                ov_ipram_raddr_mlu2ram[2:0] <=  ov_ipram_raddr_mlu2ram[2:0] + 1'b1;
                                mlu_state                   <=  mlu_state;
                            end else begin
                                ov_ipram_raddr_mlu2ram[2:0] <=  3'd0;
                                o_ipram_rd_mlu2ram          <=  1'b0;
                                o_fifo_rd_mlu2fifo          <=  1'b1;
                                mlu_state                   <=  LOOKUP_TABLE_FAIL_S;
                            end
                        end
                    end else begin
                        ov_ipram_raddr_mlu2ram[2:0] <=  3'd0;
                        o_ipram_rd_mlu2ram          <=  1'b0;
                        o_fifo_rd_mlu2fifo          <=  1'b1;
                        mlu_state                   <=  LOOKUP_TABLE_FAIL_S;
                    end
            end
            LOOKUP_TABLE_SUCCESS_S: begin
                o_fifo_rd_mlu2fifo      <= 1'b0;
                o_md_wr_mpl2tlp         <= 1'b1;
                ov_md_mpl2tlp[299:0]    <= {iv_md_fifo2mlu[299:199], ov_md_mpl2tlp[198:166],iv_md_fifo2mlu[165:0]};
                mlu_state               <= IDLE_S;
            end
            LOOKUP_TABLE_FAIL_S: begin
                o_fifo_rd_mlu2fifo      <= 1'b0;
                o_md_wr_mpl2tlp         <= 1'b1;
                if (i_unknown_multicast_forwardmode_cpe2tlp) begin
                    ov_md_mpl2tlp[299:0]    <= {iv_md_fifo2mlu[299:200], 1'b0, 1'b0, ~(32'h1 << inport), iv_md_fifo2mlu[165:0]};    //20240202
                    mlu_state               <=  IDLE_S;
                end else begin
                    ov_md_mpl2tlp[299:0]    <= {iv_md_fifo2mlu[299:200], 1'b1, 33'b0, iv_md_fifo2mlu[165:0]};
                    mlu_state               <=  IDLE_S;
                end
            end
            NOT_MULTI_IP_S: begin
                o_fifo_rd_mlu2fifo      <= 1'b0;
                ov_md_mpl2tlp[299:0]    <= iv_md_fifo2mlu[299:0];
                o_md_wr_mpl2tlp         <= 1'b1;
                mlu_state               <=  IDLE_S;
            end
            default: begin
                mlu_state               <=  IDLE_S;
                o_fifo_rd_mlu2fifo      <=  1'b0;
                o_ipram_rd_mlu2ram      <=  1'b0;
                ov_ipram_raddr_mlu2ram  <=  3'b0;
                ov_md_mpl2tlp           <=  300'b0;
                o_md_wr_mpl2tlp         <=  1'b0;
            end
        endcase
    end
end


// test signal
wire [32:0] outportbm;
wire discard;
wire [2:0] IGMP_flag;
wire [31:0] DstIP;

assign outportbm = ov_md_mpl2tlp[198:166];
assign discard = ov_md_mpl2tlp[199];
assign IGMP_flag = ov_md_mpl2tlp[266:264];
assign DstIP = ov_md_mpl2tlp[298:267];

endmodule